package com.radar.task.cd;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.radar.algorithm.Cacl;
import com.radar.algorithm.DenseLucaskanade;
import com.radar.algorithm.MathCommon;
import com.radar.algorithm.SemiLagrangian;
import com.radar.common.DateConstants;
import com.radar.common.ProductType;
import com.radar.io.CinradRadar;
import com.radar.io.RadarGridData;
import com.radar.visualize.RasterMap;
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacpp.indexer.Indexer;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.springframework.stereotype.Component;
import ucar.ma2.DataType;
import ucar.nc2.Attribute;
import ucar.nc2.Group;
import ucar.nc2.Variable;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.List;
import java.util.*;

/**
 * @author: fy-wbj
 * @description: radar task
 * @date: 2021-04-21
 */

@Component
@Slf4j
public class ForecastRunnable implements Runnable {

    /**
     * 覆盖代码注释
     * @param file
     */
    public void jobItemRun(File file) {
        String station = file.getName().split("_")[3];
        String date = file.getName().split("_")[4];
        Date dt = DateUtil.parse(date, DateConstants.DATE_TIME_FORMAT_YYYYMMDDHHMISS);
        log.info("start parse file : {} , station : {}, date: {}", file.getAbsolutePath(), station, date);
        CinradRadar cinradRadar = new CinradRadar();
        long time = System.currentTimeMillis();
        // out dir
        String outDir = Constants.tmpPath +
                File.separator +
                DateUtil.format(dt, DateConstants.DATE_FORMAT_YYYYMMDD) +
                File.separator +
                station +
                File.separator;
        FileUtil.mkdir(outDir);
        String ncFile = outDir +
                DateUtil.format(dt, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + ".nc";
        try {
            // set nc File
            cinradRadar.setNetcdfFile(ncFile);
            cinradRadar.parse(file.getAbsolutePath());
            cinradRadar.radar.setProjectStep(0.05);
            RadarGridData data = cinradRadar.radar.getCR();
            double[] alt = new double[]{500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000, 5500, 6000, 6500, 7000, 7500, 8000, 8500, 9000, 9500, 10000, 10500, 11000, 11500, 12000};
            double[] xarray = (double[]) cinradRadar.radar.netcdf.findVariable("Longitude_" + ProductType.REF.name()).read().copyToNDJavaArray();
            double[] yarray = (double[]) cinradRadar.radar.netcdf.findVariable("Latitude_" + ProductType.REF.name()).read().copyToNDJavaArray();
            log.info("cr s : {}",System.currentTimeMillis() - time);
            byte[][][] cappi = cinradRadar.radar.cappiTest(ProductType.REF, alt, xarray, yarray);
            log.info("cappi s : {}",System.currentTimeMillis() - time);
            writeCappi(cinradRadar, cappi, alt);
            double[][] decode = MathCommon.decode(data.radarData, ProductType.REF);
            for (int i = 0; i < decode.length; i++) {
                for (int j = 0; j < decode[0].length; j++) {
                    if (decode[i][j] < 5) {
                        decode[i][j] = 0;
                    }
                }
            }
            // nine median filter
            Cacl.nineMedianFilter(decode);
            // draw crgray smooth png
            RasterMap rasterMap = new RasterMap(cinradRadar.radar);
            BufferedImage bufferedImage = rasterMap.drawSingleRaster(decode, ProductType.REFGRAY, new Color(0, 0, 0, 0));
            ImageIO.write(bufferedImage, "png", new File(outDir + DateUtil.format(dt, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + "_CRGRAY_SMOOTH.png"));
            log.info("start forecast station: {},date : {},path : {}", station, DateUtil.format(dt, DateConstants.DATE_FORMAT_YYYYMMDDHHMM), file);
            try {
                forecast(outDir, cinradRadar, dt);
            } catch (Exception e1) {
                e1.printStackTrace();
                log.error("forecast error file : {}", file.getAbsolutePath());
            }

            cinradRadar.radar.flush();
        } catch (Exception e) {
            log.error("start error parse file : {} , station : {}, date: {}", file.getAbsolutePath(), station, date);
            if (cinradRadar.radar != null) {
                cinradRadar.radar.dispose();
            }
            e.printStackTrace();
        }
        log.info("start parse end time : {} ,residue: {},file : {} , station : {}, date: {}", (System.currentTimeMillis() - time) / 1000.0, Constants.singleQueue.size(), file.getAbsolutePath(), station, date);
    }

    /**
     * 覆盖代码注释
     * @param output
     * @param cinradRadar
     * @param date
     * @throws Exception
     */
    public void forecast(String output, CinradRadar cinradRadar, Date date) throws Exception {
        int offset = -1;
        List<String> pres = new ArrayList<>();
        // find pre dat
        for (int i = 6; i <= 18; i++) {
            String s = DateUtil.format(DateUtil.offsetMinute(date, -i), DateConstants.DATE_FORMAT_YYYYMMDDHHMM);
            if (FileUtil.exist(output + File.separator + s + "_CRGRAY_SMOOTH.png")) {
                pres.add(output + File.separator + s + "_CRGRAY_SMOOTH.png");
                if (offset == -1) {
                    offset = i;
                }
            }
        }
        Collections.reverse(pres);
        if (offset == -1) {
            log.info("暂为找到过去12分钟数据 station : {},date : {},path : {}", cinradRadar.radar.getRadarSite().getStation(), DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM), cinradRadar.radar.filePath);
            return;
        }
        Mat now = opencv_imgcodecs.imread(output + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + "_CRGRAY_SMOOTH.png");
        log.info("start forcast now:{}, pres:{} ", output + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + "_CRGRAY_SMOOTH.png", pres.size());
        Mat next = now.clone();
        opencv_imgproc.cvtColor(now, next, opencv_imgproc.COLOR_BGR2GRAY);
        int w = next.size().get(0);
        int h = next.size().get(1);
        double[][][] caclInput = new double[pres.size() + 1][h][w];
        double[][] ff3 = new double[h][w];
        Indexer indexer3 = next.createIndexer();
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                ff3[i][j] = indexer3.getDouble(i, j);
                if (ff3[i][j] == 255) {
                    ff3[i][j] = 0;
                }
            }
        }
        caclInput[caclInput.length - 1] = ff3;
        int index = 0;
        for (String f : pres) {
            Mat ff1 = opencv_imgcodecs.imread(f);
            Mat a = now.clone();
            opencv_imgproc.cvtColor(ff1, a, opencv_imgproc.COLOR_BGR2GRAY);
            double[][] ff2 = new double[h][w];
            Indexer indexer = a.createIndexer();
            for (int i = 0; i < h; i++) {
                for (int j = 0; j < w; j++) {
                    ff2[i][j] = indexer.getDouble(i, j);
                    if (ff2[i][j] == 255) {
                        ff2[i][j] = 0;
                    }
                }
            }
            caclInput[index] = ff2;
            index++;
        }
        double[][][] cacl = DenseLucaskanade.cacl(caclInput);
        log.info("end flow now:{}, pres:{} ", output + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + "_CRGRAY_SMOOTH.png", pres.size());
        double[][] u = cacl[0];
        double[][] v = cacl[1];
        for (int i = 0; i < h; i++) {
            for (int j = 0; j < w; j++) {
                u[i][j] = u[i][j] * (6.0 / offset);
                v[i][j] = v[i][j] * (6.0 / offset);
            }
        }
        RadarGridData cr = cinradRadar.radar.getCR();
        double[][] decode = MathCommon.decode(cr.radarData, ProductType.REF);
        SemiLagrangian semiLagrangian = new SemiLagrangian();
        int t = 20;
        float[][][] extrapolate = semiLagrangian.extrapolate(MathCommon.doubleToFloat(decode), MathCommon.doubleToFloat(u), MathCommon.doubleToFloat(v), t);
        for (int i = 0; i < t; i++) {
            FileUtil.mkdir(output + File.separator + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM));
            RasterMap rasterMap = new RasterMap(cinradRadar.radar);
            BufferedImage bufferedImage = rasterMap.drawSingleRaster(MathCommon.floatToDouble(extrapolate[i]), new Color(0, 0, 0, 0));
            ImageIO.write(bufferedImage, "png", new File(output + File.separator + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + File.separator + "CR_" + i + ".png"));
        }
        log.info("end SemiLagrangian now:{}, pres:{} ", output + DateUtil.format(date, DateConstants.DATE_FORMAT_YYYYMMDDHHMM) + "_CRGRAY_SMOOTH.png", pres.size());
        cinradRadar.radar.writeSemiLagrangian(extrapolate, offset, t);
    }

    /**
     * 覆盖代码注释
     * @param cinradRadar
     * @param cappi
     * @param altArray
     */
    public void writeCappi(CinradRadar cinradRadar, byte[][][] cappi, double[] altArray) {
        cinradRadar.radar.write.addDimension((Group) null, "Altitude", altArray.length);
        Variable v1 = cinradRadar.radar.write.addVariable((Group) null, "Altitude", DataType.DOUBLE, "Altitude");
        v1.addAttribute(new Attribute("units", "m"));
        v1.addAttribute(new Attribute("actual_range", Arrays.asList(altArray[0], altArray[altArray.length - 1])));
        v1.setCachedData(MathCommon.toArray(altArray));
        Variable variable = cinradRadar.radar.write.addVariable((Group) null, "CAPPI", DataType.BYTE, Arrays.asList(
                cinradRadar.radar.netcdf.findDimension("Altitude"),
                cinradRadar.radar.netcdf.findDimension("Latitude_" + ProductType.REF.name()),
                cinradRadar.radar.netcdf.findDimension("Longitude_" + ProductType.REF.name())));
        variable.addAttribute(new Attribute("missing_value", -999.0F));
        variable.addAttribute(new Attribute("full_name", ProductType.CR.getFullName() + "_CAPPI"));
        variable.addAttribute(new Attribute("units", ProductType.CR.getUnit()));
        variable.setCachedData(MathCommon.toByteArray(cappi));
    }

    @Override
    /**
     * 覆盖代码注释
     */
    public void run() {
        while (true) {
            try {
                if (!Constants.singleQueue.isEmpty()) {
                    File poll = Constants.singleQueue.poll();
                    if (poll.exists()) {
                        jobItemRun(poll);
                    }
                } else {
                    Thread.sleep(1000);
                }
            } catch (Exception e) {
                log.error(e.getMessage() + "cd item 解析异常出现异常！");
                e.printStackTrace();
            }
        }

    }

    /**
     * 覆盖代码注释
     * @param args
     */
    public static void main(String[] args) {
        new ForecastRunnable().jobItemRun(new File("E:\\data\\radar/radardata/Z9090/Z_RADR_I_Z9090_20210716015513_O_DOR_SB_CAP_FMT.bin"));
    }
}
